package com.lc.my_springframework.beans.factory.support;

import com.lc.my_springframework.beans.BeansException;
import com.lc.my_springframework.beans.PropertyValue;
import com.lc.my_springframework.beans.PropertyValues;
import com.lc.my_springframework.beans.factory.BeanFactory;
import com.lc.my_springframework.beans.factory.config.BeanDefinition;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class DefaultBeanFactory extends AbstractBeanFactory implements BeanFactory, BeanDefinitionRegistry {
    private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap();

    @Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) {
        try {
            Class<?> beanClass = beanDefinition.getClazz();
            Constructor constructorToUse = getConstructor(beanClass, args);
            Object instance = constructorToUse.newInstance(args);
            populateBean(beanName, beanDefinition,instance);
            return instance;
        } catch (Exception e) {
            e.printStackTrace();
            throw new BeansException("bean 初始化失败", e);
        }

    }

    private void populateBean(String beanName, BeanDefinition beanDefinition, Object instance) {
        final PropertyValues propertyValues = beanDefinition.getPropertyValues();
        if (propertyValues != null) {
            for (PropertyValue propertyValue : Arrays.asList(propertyValues.getPropertyValues())) {
                try {
                    Field field =instance.getClass().getDeclaredField(propertyValue.getName());
                    field.setAccessible(true);
                    field.set(instance,propertyValue.getValue());
                } catch (NoSuchFieldException | IllegalAccessException e) {
                    e.printStackTrace();
                    throw new RuntimeException(e);
                }
            }
        }
    }

    @Override
    protected BeanDefinition getBeanDefinition(String name) {
        return this.beanDefinitionMap.get(name);
    }

    @Override
    public void registerBeanDefinition(String name, BeanDefinition beanDefinition) throws BeansException {
        beanDefinitionMap.put(name, beanDefinition);
    }

    private Constructor getConstructor(Class<?> beanClass, Object[] args) throws NoSuchMethodException {
        Constructor constructorToUse = null;
        if (args == null) {
            return beanClass.getDeclaredConstructor();
        }
        Constructor<?>[] declaredConstructors = beanClass.getDeclaredConstructors();
        for (Constructor ctor : declaredConstructors) {
            if (ctor.getParameterTypes().length == args.length) {
                constructorToUse = ctor;
                break;
            }
        }
        return constructorToUse;
    }
}
